﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Threading;
using System.Windows.Forms;

namespace EcanPicTools
{
    public partial class frmTools : Form
    {
        #region 私有变量 Private Fields

        /// <summary>
        /// 压缩失败张数
        /// </summary>
        private static int comfial;

        /// <summary>
        /// 是否正在压缩
        /// </summary>
        private static bool iscompress;

        /// <summary>
        /// 图片像素比例
        /// </summary>
        private static double percentpixel;

        /// <summary>
        /// 图片压缩比例
        /// </summary>
        private static int percentcom;

        /// <summary>
        /// 图片总和
        /// </summary>
        private static int filecount;

        /// <summary>
        /// 所有需要压缩的图片路径集合
        /// </summary>
        private static List<FileInfoU> fileInfoUs;

        /// <summary>
        /// 用于控制多线程有序移除fileInfoUs的元素
        /// </summary>
        private readonly object mark = new object();

        #endregion

        #region 代理 Delegate

        private delegate void DelUserHandler(object obj);
        private delegate void DelShowMessage(string title, string content);
        #endregion

        #region 构造函数 Constructor

        public frmTools()
        {
            InitializeComponent();
            fileInfoUs = new List<FileInfoU>();
        }

        #endregion

        #region 事件 EventArgs

        /// <summary>
        /// 窗体加载
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void frmTools_Load(object sender, EventArgs e)
        {
            //toolStrip1.Visible = false;
            tsPrompt.Text = string.Empty;
            tsProgressBar.Visible = false;
            timer1.Start();
        }

        /// <summary>
        /// Timer事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void timer1_Tick(object sender, EventArgs e)
        {
            if (iscompress)
            {
                tsPrompt.Text = "正在压缩，请稍后···";
                tsComCount.Text = (filecount - fileInfoUs.Count).ToString();//已压缩数量
                tsProgressBar.Visible = true;
                tsProgressBar.Value = (filecount - fileInfoUs.Count)*100/filecount;
            }
            else
            {
                tsProgressBar.Visible = false;
            }
        }

        /// <summary>
        /// 文件拖放完成发生
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void lboxPicPath_DragDrop(object sender, DragEventArgs e)
        {
            string fname = ((Array)e.Data.GetData(DataFormats.FileDrop)).GetValue(0).ToString();
            if (fname.LastIndexOf(".").Equals(-1))
            {//文件夹
                if (Directory.Exists(fname))
                {
                    GetFile(fname, fname);
                }
                return;
            }
            if(File.Exists(fname))
            {
                string suffix = fname.Substring(fname.LastIndexOf(".") + 1).ToLower();
                const string allsuffix = "jpg|gif|png|bmp|jpeg";
                if (allsuffix.IndexOf(suffix).Equals(-1))
                {
                    return;
                }
                FileInfoU fileInfoU = new FileInfoU();
                fileInfoU.OldFullpath = fname;
                fileInfoU.OldFolderpath = fname.Substring(0, fname.LastIndexOf("\\"));
                AddFileInfoU(fileInfoU);
            }
        }

        /// <summary>
        /// 拖放文件时发生
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void lboxPicPath_DragEnter(object sender, DragEventArgs e)
        {
            e.Effect = e.Data.GetDataPresent(DataFormats.FileDrop) ? DragDropEffects.All : DragDropEffects.None;
        }

        #region 按钮事件 Button EventArgs

        /// <summary>
        /// 添加文件夹[将文件夹中的所有图片都遍历出来(包括子文件夹)]
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void msAddFolder_Click(object sender, EventArgs e)
        {
            FolderBrowserDialog fbd = new FolderBrowserDialog();
            if (fbd.ShowDialog() == DialogResult.OK)
            {
                GetFile(fbd.SelectedPath, fbd.SelectedPath);
            }
        }

        /// <summary>
        /// 添加单个图片文件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void msAddImgFile_Click(object sender, EventArgs e)
        {
            GetFile();
        }

        /// <summary>
        /// 清空图片集合
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void msClearlbox_Click(object sender, EventArgs e)
        {
            fileInfoUs.Clear();
            lboxPicPath.Items.Clear();
            InvokeSettsAllCountText(null);
        }

        /// <summary>
        /// 删除选中图片
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void msRemoveSelected_Click(object sender, EventArgs e)
        {
            if (lboxPicPath.SelectedItems.Count > 0)
            {
                for (int i = lboxPicPath.SelectedItems.Count - 1; i >= 0; i--)
                {
                    RemoveFileInfoU(lboxPicPath.SelectedItems[i].ToString());
                    lboxPicPath.Items.Remove(lboxPicPath.SelectedItems[i]);
                    InvokeSettsAllCountText(null);
                }
            }
            else
            {
                InvokeMessageBoxShow("提示","请选择要移除的文件");
            }
        }

        private void msHelp_Click(object sender, EventArgs e)
        {
            frmHelp frmHelp = new frmHelp();
            frmHelp.ShowDialog();
        }

        /// <summary>
        /// 选择保存文件夹路径
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnShowSavePath_Click(object sender, EventArgs e)
        {
            FolderBrowserDialog fbd = new FolderBrowserDialog();
            fbd.Description = "请选择保存输出图像路径";
            fbd.ShowNewFolderButton = true;
            if (fbd.ShowDialog() == DialogResult.OK)
            {
                if (fbd.SelectedPath != "")
                {
                    txtSavePath.Text = fbd.SelectedPath;
                }
            }
        }

        /// <summary>
        /// 开始压缩图片
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void msStartEcanpic_Click(object sender, EventArgs e)
        {
            string savepath = txtSavePath.Text.Trim();
            bool isRename;
            if (string.IsNullOrEmpty(savepath))
            {
                btnShowSavePath.Focus();
                InvokeMessageBoxShow("提示", "请选择保存路径");
                return;
            }
            if (!Directory.Exists(savepath))
            {
                btnShowSavePath.Focus();
                InvokeMessageBoxShow("提示", "文件夹路径不存在、路径错误！");
                return;
            }
            if (int.TryParse(txtPercentCom.Text, out percentcom))
            {
                if (percentcom <= 0 || percentcom > 100)
                {
                    txtPercentCom.Focus();
                    InvokeMessageBoxShow("提示", "压缩比值介于1~100");
                    return;
                }
            }
            else
            {
                txtPercentCom.Focus();
                InvokeMessageBoxShow("提示", "请输入正确的压缩比值！");
                return;
            }
            if (double.TryParse(txtPercentPixel.Text, out percentpixel))
            {
                if (percentcom <= 0 || percentcom > 100)
                {
                    txtPercentPixel.Focus();
                    InvokeMessageBoxShow("提示", "像素比值介于1~100");
                    return;
                }
            }
            else
            {
                txtPercentPixel.Focus();
                InvokeMessageBoxShow("提示", "请输入正确的像素比值！");
                return;
            }
            if (fileInfoUs.Count <= 0)
            {
                InvokeMessageBoxShow("提示", "请选择要压缩的图片！");
                return;
            }
            isRename = radioReName.Checked;
            for (int i = 0; i < fileInfoUs.Count; i++)
            {
                fileInfoUs[i].NewFolderpath = savepath;
                fileInfoUs[i].IsRename = isRename;
            }
            //显示压缩进度等信息
            percentcom = 100 - percentcom;
            filecount = fileInfoUs.Count;
            toolStrip1.Visible = true;
            tsAllCount.Text = filecount.ToString();
            CompactPics();
        }

        #endregion

        #endregion

        #region 内部方法 Internal Methods

        /// <summary>
        /// 用多线程压缩图片
        /// </summary>
        /// <param name="obj"></param>
        private void CompactPic(object obj)
        {
            FileInfoU fileInfoU = (FileInfoU)obj;
            bool flag = Thumbnail.GetThumbnail(fileInfoU.OldFullpath, fileInfoU.NewFullpath, percentpixel, percentcom);
            RemoveFile(fileInfoU,flag);
        }

        /// <summary>
        /// 压缩所有图片
        /// </summary>
        private void CompactPics()
        {
            iscompress = true;
            InvokeBtnEnable(false);
            foreach (FileInfoU fileInfoU in fileInfoUs)
            {
                ThreadPool.QueueUserWorkItem(CompactPic, fileInfoU);
            }
        }

        /// <summary>
        /// 根据路径删除FileInfoU
        /// </summary>
        /// <param name="fullpath"></param>
        private void RemoveFileInfoU(string fullpath)
        {
            for (int i = 0; i < fileInfoUs.Count; i++)
            {
                if (fileInfoUs[i].OldFullpath.Equals(fullpath))
                {
                    fileInfoUs.Remove(fileInfoUs[i]);
                    break;
                }
            }
        }

        /// <summary>
        /// 增加一张图片路径到lbox和FileInfoU
        /// </summary>
        /// <param name="fileInfoU"></param>
        private void AddFileInfoU(FileInfoU fileInfoU)
        {
            fileInfoUs.Add(fileInfoU);
            InvokelboxPicPathAddItem(fileInfoU.OldFullpath);
        }

        /// <summary>
        /// 获取单个图片文件 并添加到 lbox
        /// </summary>
        private void GetFile()
        {
            OpenFileDialog open = new OpenFileDialog();
            open.Title = "请选择要压缩的图片";
            open.Filter = "图片文件(*.jpg,*.bmp,*.png,*.gif,*.jpeg,*.tif)|*.jpg;*.bmp;*.png;*.gif;*.jpeg;*.tif";
            open.Multiselect = true;
            if (open.ShowDialog() == DialogResult.OK)
            {
                foreach (string file in open.FileNames)
                {
                    FileInfoU fileInfoU = new FileInfoU();
                    fileInfoU.OldFullpath = file;
                    fileInfoU.OldFolderpath = file.Substring(0, file.LastIndexOf("\\"));
                    AddFileInfoU(fileInfoU);
                }
            }
        }

        /// <summary>
        /// 递归获取文件夹中的所有图片路径 并添加到 lbox
        /// </summary>
        /// <param name="folderpath">文件夹路径</param>
        /// <param name="oldfolderpath">基本路径</param>
        private void GetFile(string folderpath,string oldfolderpath)
        {
            DirectoryInfo pic = new DirectoryInfo(folderpath);
            string[] suffixs = new[] { "*.jpg", "*.bmp", "*.png", "*.gif", "*.jpeg" };
            foreach (string suffix in suffixs)
            {
                foreach (FileInfo file in pic.GetFiles(suffix))
                {
                    FileInfoU fileInfoU = new FileInfoU();
                    fileInfoU.OldFullpath = file.FullName;
                    fileInfoU.OldFolderpath = oldfolderpath;
                    AddFileInfoU(fileInfoU);
                }
            }
            string[] dirs = Directory.GetDirectories(folderpath);//获取子目录名
            for (int j = 0; j < dirs.Length; j++)
            {
                GetFile(dirs[j], oldfolderpath);
            }
        }

        /// <summary>
        /// 移除fileInfoUs中的元素 和 移除lbox中的元素
        /// </summary>
        /// <param name="fileInfoU"></param>
        /// <param name="flag">是否压缩成功</param>
        private void RemoveFile(FileInfoU fileInfoU,bool flag)
        {
            lock (mark)
            {
                fileInfoUs.Remove(fileInfoU);
                InvokelboxPicPathRemoveItem(fileInfoU.OldFullpath);
                if (!flag)
                {
                    comfial += 1;
                }
                if (fileInfoUs.Count == 0)
                {//压缩完成
                    InvokeMessageBoxShow("提示", "压缩完成！");
                    iscompress = false;
                    filecount = 0;
                    InvokeBtnEnable(true);
                    if (comfial>0)
                    {
                        InvoketsPrompText("压缩完成，失败 " + comfial + " 张！");
                        InvoketsPrompColor(Color.Red);
                    }
                    else
                    {
                        InvoketsPrompText("全部压缩成功！");
                    }
                }
            }
        }

        #endregion

        #region 所有Invoke函数

        private void InvoketsPrompText(object obj)
        {
            if (toolStrip1.InvokeRequired)
            {
                DelUserHandler delUserHandler = InvoketsPrompText;
                Invoke(delUserHandler, obj);
            }
            else
            {
                tsPrompt.Text = obj.ToString();
            }
        }

        private void InvoketsPrompColor(object obj)
        {
            if (toolStrip1.InvokeRequired)
            {
                DelUserHandler delUserHandler = InvoketsPrompText;
                Invoke(delUserHandler, obj);
            }
            else
            {
                tsPrompt.ForeColor = (Color) obj;
            }
        }

        private void InvokeBtnEnable(object obj)
        {
            if (toolStrip1.InvokeRequired)
            {
                DelUserHandler delUserHandler = InvokeBtnEnable;
                Invoke(delUserHandler,obj);
            }
            else
            {
                ToolStripItemCollection items = menuStrip1.Items;
                for (int i = 0; i < items.Count; i++)
                {
                    items[i].Enabled = (bool)obj;
                }
                btnShowSavePath.Enabled = (bool)obj;
                btnStartEcanPic.Enabled = (bool)obj;
            }
        }

        /// <summary>
        /// 弹出消息对话框
        /// </summary>
        /// <param name="title">消息标题</param>
        /// <param name="content">消息内容</param>
        private void InvokeMessageBoxShow(string title, string content)
        {
            if (InvokeRequired)
            {
                DelShowMessage showMessage = InvokeMessageBoxShow;
                Invoke(showMessage, title, content);
            }
            else
            {
                MessageBox.Show(content, title, MessageBoxButtons.OK, MessageBoxIcon.Information);
            }
        }

        /// <summary>
        /// lboxPicPath添加Item
        /// </summary>
        /// <param name="obj"></param>
        private void InvokelboxPicPathAddItem(object obj)
        {
            if (lboxPicPath.InvokeRequired)
            {
                DelUserHandler delUserHandler = InvokelboxPicPathAddItem;
                Invoke(delUserHandler, obj);
            }
            else
            {
                lboxPicPath.Items.Add(obj.ToString());
                tsAllCount.Text = lboxPicPath.Items.Count.ToString();
                tsComCount.Text = "0";
                tsPrompt.Text = string.Empty;
            }
        }

        /// <summary>
        /// 设置图片总数
        /// </summary>
        /// <param name="obj"></param>
        private void InvokeSettsAllCountText(object obj)
        {
            if (toolStrip1.InvokeRequired)
            {
                DelUserHandler delUserHandler = InvokeSettsAllCountText;
                Invoke(delUserHandler);
            }
            else
            {
                tsAllCount.Text = lboxPicPath.Items.Count.ToString();
            }
        }

        /// <summary>
        /// lboxPicPath移除Item
        /// </summary>
        /// <param name="obj"></param>
        private void InvokelboxPicPathRemoveItem(object obj)
        {
            if (lboxPicPath.InvokeRequired)
            {
                DelUserHandler delUserHandler = InvokelboxPicPathRemoveItem;
                Invoke(delUserHandler, obj);
            }
            else
            {
                lboxPicPath.Items.Remove(obj);
            }
        }

        #endregion
        
    }
}
